﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using UnityEngine;
using UnityEngine.Networking;

namespace BToolkit
{
    public class IPServer
    {
        static Dictionary<int, List<int[]>> chinaIps = new Dictionary<int, List<int[]>>();
        static bool hadCallbackSuccess;
        static string CacheFilePath { get { return Application.persistentDataPath + "/" + "iplib"; } }

        /// <summary>
        /// 是否是中国大陆的IP
        /// </summary>
        /// <param name="localIPLib">本地IP库（可传空），为了绕过漫长的IP库下载，首次可先传入本地IP库，之后程序会在空闲时下载最新库到沙盒，之后只使用沙盒的最新库</param>
        public static void IsChinaMainlandIP(string ip, Action<bool> onSuccess, Action onTimeout, string localIPLib = null)
        {
            if (chinaIps.Count > 0)
            {
                bool b = _IsChinaMainlandIP(ip);
                if (onSuccess != null) onSuccess(b);
            }
            else
            {
                BUtils.Instance.StartCoroutine(LoadIPLib(ip, onSuccess, localIPLib));
                //20秒超时
                hadCallbackSuccess = false;
                BTimer.Invoke(20, () =>
                {
                    if (!hadCallbackSuccess && onTimeout != null) onTimeout();
                });
            }
        }

        static IEnumerator LoadIPLib(string ip, Action<bool> callback, string localIPLib)
        {
            string ipLib = null;
            if (File.Exists(CacheFilePath))
            {
                ipLib = File.ReadAllText(CacheFilePath);
                Parse(ipLib, ip, callback);
            }
            else
            {
                ipLib = localIPLib;
                File.WriteAllText(CacheFilePath, ipLib);
                Parse(ipLib, ip, callback);
            }

            //程序已执行Parse，结束后即回调，以下加载只为准备最新的版本库以便下次使用
            UnityWebRequest request = UnityWebRequest.Get("http://ftp.apnic.net/apnic/stats/apnic/delegated-apnic-latest");
            yield return request.SendWebRequest();
            if (request.error != null)
            {
                Debuger.LogError("下载IP库失败:" + request.error);
                if (callback != null) callback(false);
                yield break;
            }
            ipLib = request.downloadHandler.text;
            File.WriteAllText(CacheFilePath, ipLib);
        }

        static void Parse(string downloadText, string ip, Action<bool> callback)
        {
            try
            {
                // ip格式: add1.add2.add3.add4
                // key为 : add1*256+add2
                // value为int[2]: int[0]存的add3*256+add4的开始ip int[4]存的结束ip
                Dictionary<int, List<int[]>> map = new Dictionary<int, List<int[]>>();

                string[] lines = downloadText.Split('\n');
                foreach (string line in lines)
                {
                    if (line.StartsWith("apnic|CN|ipv4|"))
                    {
                        // 只处理属于中国的ipv4地址
                        string[] strs = line.Split(new string[] { "\\", "|" }, StringSplitOptions.RemoveEmptyEntries);
                        string _ip = strs[3];
                        string[] add = _ip.Split(new string[] { "\\", "." }, StringSplitOptions.RemoveEmptyEntries);
                        int count = int.Parse(strs[4]);

                        int startIp = int.Parse(add[0]) * 256 + int.Parse(add[1]);
                        while (count > 0)
                        {
                            if (count >= 65536)
                            {
                                // add1,add2 整段都是中国ip
                                chinaIps.Add(startIp, new List<int[]>());
                                count -= 65536;
                                startIp += 1;
                            }
                            else
                            {

                                int[] ipRange = new int[2];
                                ipRange[0] = int.Parse(add[2]) * 256 + int.Parse(add[3]);
                                ipRange[1] = ipRange[0] + count;
                                count -= count;

                                List<int[]> list = null;
                                if (map.ContainsKey(startIp))
                                {
                                    list = map[startIp];
                                }
                                if (list == null)
                                {
                                    list = new List<int[]>();
                                    map.Add(startIp, list);
                                }
                                list.Add(ipRange);
                            }
                        }
                    }
                }
                chinaIps = chinaIps.Concat(map).ToDictionary(x => x.Key, x => x.Value); ;
            }
            catch (Exception e)
            {
                Debuger.LogError(e);
            }

            if (callback != null)
            {
                bool b = _IsChinaMainlandIP(ip);
                callback(b);
                hadCallbackSuccess = true;
            }
        }

        static bool _IsChinaMainlandIP(string ip)
        {
            if (string.IsNullOrEmpty(ip))
            {
                return false;
            }
            string[] strs = ip.Split(new string[] { "\\", "." }, StringSplitOptions.RemoveEmptyEntries);
            if (strs.Length != 4)
            {
                return false;
            }
            int key = int.Parse(strs[0]) * 256 + int.Parse(strs[1]);
            List<int[]> list = null;
            if (chinaIps.ContainsKey(key))
            {
                list = chinaIps[key];
            }
            if (list == null)
            {
                return false;
            }
            if (list.Count == 0)
            {
                // 整段都是中国ip
                return true;
            }
            int ipValue = int.Parse(strs[2]) * 256 + int.Parse(strs[3]);
            foreach (int[] ipRange in list)
            {
                if (ipValue >= ipRange[0] && ipValue <= ipRange[1])
                {
                    return true;
                }
            }
            return false;
        }
    }
}